from twisted.protocols import basic
from twisted.internet.protocol import Protocol, ClientFactory, ServerFactory
import struct
import zlib

def unpack_N(fmt, bytes) :
    try :
        return struct.unpack(fmt, bytes)
    except:
        return None

def pack_N(fmt, *args) :
    try :
        return struct.pack(fmt, *args)
    except:
        return None

def unpack_int(bytes) :
    return unpack_N('!i', bytes)

def unpack_short(bytes) :
    return unpack_N('!h', bytes)

def pack_int(int_value) :
    return pack_N('!i', int_value)

def pack_short(short_value) :
    return pack_N('!h', short_value)

def cal_crc32(bytes) :
    return zlib.crc32(bytes)

class CallbackHandler :

class PbHandler :
    #key: class.func/interface.func, value: type
    _typeMap = {}
    #key: signature(ip:port), value: object created via other side's request
    _objPool = {}
    func_delimiter = b'\0'
    referenceCount = 0

    def marshalPB(self, funcType, reference, numField, args, callback):
        payload = ''
        payload += funcType
        payload +=

    def unmarshalPB(self, data, signature):
        data, digest = data[:-4], unpack_int(data[-4:])
        if cal_crc32(data) != digest :
            #check sum error
            pass

        funcType, data = data.split(self.func_delimiter, 1)
        reference, data = unpack_int(data[:4]), data[4:]
        numField, data = unpack_short(data[:2]), data[2:]
        offsetsBuf, data = data[:4*(numField-1)], data[4*(numField-1):]
        offsets = [unpack_int(offsetsBuf[b:e]) for b, e in zip(range(0, len(offsetsBuf), 4), range(0, len(offsetsBuf), 4)[1:] + [len(offsetsBuf)]) ]
        args = [data[b:e] for b, e in zip(([0] + offsets), (offsets + len(data))) ]
        if reference == 0 :
            #create object, the funcType should be a object type
            reference = self.createObj(funcType)
            if reference :
                # create object successful, return reference to client
                pass
            else :
                # create object failed
                pass
        else :
            if reference in self._objPool :
                obj = self._objPool[reference]
                ret = obj.dispatch(funcType, args)
            else :
                #can't find referenced object
                pass

    def createObj(self, objType):
        if objType in self._typeMap :
            obj = self._typeMap[objType]()
            self.referenceCount += 1
            self._objPool[self.referenceCount] = obj
            return self.referenceCount
        else :
            return None

    def setSupportedTypes(self, types):
        self._typeMap = types


class SORBProtocol(Protocol, basic._PauseableMixin, PbDispatcher) :
    """
    A protocol that receives lines and/or raw data, depending on mode.

    In line mode, each line that's received becomes a callback to
    L{lineReceived}.  In raw data mode, each chunk of raw data becomes a
    callback to L{rawDataReceived}.  The L{setLineMode} and L{setRawMode}
    methods switch between the two modes.

    This is useful for line-oriented protocols such as IRC, HTTP, POP, etc.

    @cvar delimiter: The line-ending delimiter to use. By default this is
                     C{b'\\r\\n'}.
    @cvar MAX_LENGTH: The maximum length of a line to allow (If a
                      sent line is longer than this, the connection is dropped).
                      Default is 16384.
    """
    line_mode = 1
    # supported mode: pb, raw, line
    mode = 'pb'
    _buffer = b''
    _busyReceiving = False
    _pblen = 0
    delimiter = b'\r\n'
    MAX_LENGTH = 16384

    def clearLineBuffer(self):
        """
        Clear buffered data.

        @return: All of the cleared buffered data.
        @rtype: C{bytes}
        """
        b, self._buffer = self._buffer, b""
        return b


    def dataReceived(self, data):
        """
        Protocol.dataReceived.
        Translates bytes into lines, and calls lineReceived (or
        rawDataReceived, depending on mode.)
        """
        if self._busyReceiving:
            self._buffer += data
            return

        try:
            self._busyReceiving = True
            self._buffer += data
            while self._buffer and not self.paused:
                if self.mode == 'line':
                    try:
                        line, self._buffer = self._buffer.split(self.delimiter, 1)
                    except ValueError:
                        if len(self._buffer) > self.MAX_LENGTH:
                            line, self._buffer = self._buffer, b''
                            return self.lineLengthExceeded(line)
                        return
                    else:
                        lineLength = len(line)
                        if lineLength > self.MAX_LENGTH:
                            exceeded = line + self._buffer
                            self._buffer = b''
                            return self.lineLengthExceeded(exceeded)
                        why = self.lineReceived(line)
                        if (why or self.transport and self.transport.disconnecting):
                            return why
                elif self.mode == 'raw':
                    data = self._buffer
                    self._buffer = b''
                    why = self.rawDataReceived(data)
                    if why:
                        return why
                else :
                    #protobuf mode
                    #parse message length
                    if self._pblen == 0:
                        if len(self._buffer) >= 4 :
                            self._pblen = unpack_int(self._buffer[0:4])
                            if not self._pblen or _pblen > self.MAX_LENGTH :
                                pb, self._buffer = self._buffer, b''
                                return self.lineLengthExceeded(pb)
                            self._buffer = self._buffer[4:]

                    #message length is not parsed or buffered data is not enough for this message
                    if self._pblen == 0 or self._pblen > len(self._buffer) :
                        return
                    else :
                        pbBuf = self._buffer[0:self._pblen]
                        self._buffer = self._buffer[self._pblen:]
                        self.pbReceived(pbBuf)
        finally:
            self._busyReceiving = False

    def setLineMode(self, extra=b''):
        """
        Sets the line-mode of this receiver.

        If you are calling this from a rawDataReceived callback,
        you can pass in extra unhandled data, and that data will
        be parsed for lines.  Further data received will be sent
        to lineReceived rather than rawDataReceived.

        Do not pass extra data if calling this function from
        within a lineReceived callback.
        """
        self.mode = 'line'
        if extra:
            return self.dataReceived(extra)


    def setRawMode(self):
        """
        Sets the raw mode of this receiver.
        Further data received will be sent to rawDataReceived rather
        than lineReceived.
        """
        self.mode = 'raw'

    def setPbMode(self):
        self.mode = 'pb'


    def rawDataReceived(self, data):
        """
        Override this for when raw data is received.
        """
        raise NotImplementedError

    def lineReceived(self, line):
        """
        Override this for when each line is received.

        @param line: The line which was received with the delimiter removed.
        @type line: C{bytes}
        """
        raise NotImplementedError

    def pbReceived(self, pbBuf):
        raise NotImplementedError

    def sendLine(self, line):
        """
        Sends a line to the other end of the connection.

        @param line: The line to send, not including the delimiter.
        @type line: C{bytes}
        """
        return self.transport.write(line + self.delimiter)

    def sendBuf(self, buf):
        return self.transport.write(buf)

    def lineLengthExceeded(self, line):
        """
        Called when the maximum line length has been reached.
        Override if it needs to be dealt with in some special way.

        The argument 'line' contains the remainder of the buffer, starting
        with (at least some part) of the line which is too long. This may
        be more than one line, or may be only the initial portion of the
        line.
        """
        return self.transport.loseConnection()

    def connectionMade(self):
        pass

    def connectionLost(self, reason):
        self.factory.connectionLost(self.transport.getPeer())


class SORBServerFactory(ServerFactory):
    protocol = SORBProtocol

    def __init__(self):
        self.connections = {}

    def buildProtocol(self, addr):
        p = self.protocol()
        p.factory = self
        self.connections[addr] = p
        return p

    def connectionLost(self, addr):
        if addr in self.connections :
            del self.connections[addr]


class SORBClientFactory(ClientFactory) :
    protocol = SORBProtocol




